let defaultId = new Date().getTime()
export default class Node {
  constructor(options) {
    this.id = defaultId++
    this.checked = false
    this.indeterminate = false
    this.data = null
    this.expanded = false
    this.parent = null
    this.isLeaf = false
    for (const name in options) {
      if (options.hasOwnProperty(name)) {
        this[name] = options[name]
      }
    }
    this.select = false
    this.loaded = false
    this.childNodes = []
    this.loading = false
    this.parent ? this.level = this.parent.level + 1 : this.level = 0
    this.update(this.data)
  }
  update(data) {
    const { prop } = this.tree
    let children

    this.markData(data)

    if (this.key !== null) {
      this.id = this.key
    }

    if (Array.isArray(data) && this.level === 0) {
      children = data
      this.id = 'root'
    } else if (data.hasOwnProperty(prop.children)) {
      children = data[prop.children]
    } else {
      if (!this.tree.lazy) this.isLeaf = true
    }

    if (Array.isArray(children) && children.length > 0) {
      children.forEach(data => {
        this.appendChild(data)
      })
    } else {
      if (!this.tree.lazy) this.isLeaf = true
    }
    this.tree.nodesMap.set(this.id, this)
  }
  getTrueData(prop) {
    const props = this.tree.prop[prop]
    const data = this.data
    if (data.hasOwnProperty(props)) {
      return data[props]
    } else if (data.hasOwnProperty(prop)) {
      return data[prop]
    } else {
      return null
    }
  }
  get label() {
    return this.getTrueData('label')
  }
  get key() {
    return this.getTrueData('key')
  }
  get disabled() {
    return this.getTrueData('disabled')
  }
  get check() {
    return this.checked
  }
  set check(val) {
    if (val !== this.checked) {
      this.checked = val
      this.handleCheckChange()
    }
  }
  get siblingsNodes() {
    const arr = []
    const currentNode = this
    this.parent.childNodes.forEach(node => {
      if (node.id !== currentNode.id) {
        arr.push(node)
      }
    })
    return arr
  }
  get checkedChildNodes() {
    const arr = []
    this.childNodes.forEach(node => {
      if (node.checked) arr.push(node)
    })
    return arr
  }
  get indeterminateChildNodes() {
    const arr = []
    this.childNodes.forEach(node => {
      if (node.checked || node.indeterminate) arr.push(node)
    })
    return arr
  }
  markData(data) {
    Object.defineProperty(data, '_node', {
      value: this,
      enumerable: false,
      configurable: false,
      writable: false
    })
    if (!Array.isArray(data)) {
      Object.defineProperty(data, '_nodeId', {
        value: this.id,
        enumerable: false,
        configurable: false,
        writable: false
      })
    } else if (this.level === 0) {
      Object.defineProperty(data, '_nodeId', {
        value: 'root',
        enumerable: false,
        configurable: false,
        writable: false
      })
    }
  }
  get isExpanded() {
    return this.expanded
  }
  set isExpanded(value) {
    this.changeExpanded(value)
  }
  changeExpanded(value) {
    if (this.tree.lazy && !this.loaded) {
      this.loadNode()
    } else {
      this.expanded = value
    }
    if (this.tree.accordion && value) {
      this.siblingsNodes.forEach(node => {
        node.expanded = false
      })
    }
  }
  loadNode() {
    const node = this
    if (node.loaded || node.loading || !node.tree.lazy || node.childNodes.length > 0) {
      return
    }
    node.loading = true
    node.tree.load(node.data, (data) => {
      if (!Array.isArray(data)) throw new Error('传入子元素必须是数组')
      data.forEach(data => {
        node.appendChild(data)
      })
      node.loading = false
      node.loaded = true
      node.check = false
      node.expanded = true
    })
  }
  getPath() {
    const arr = []
    let n = 0
    let node = this
    if (Array.isArray(this.tree.root)) {
      n = 1
    }
    while (node.level > n) {
      arr.push(node.data)
      node = node.parent
    }
    return arr
  }
  handleCheckChange() {
    const checked = this.checked
    this.childNodes.forEach(node => {
      node.check = checked
    })
    if (this.parent) {
      this.parent.handleChildCheckChange()
    }
  }
  handleChildCheckChange() {
    if (Array.isArray(this.data) || this.level === 0) {
      return
    }
    if (this.childNodes.length === this.checkedChildNodes.length && this.childNodes.length !== 0) {
      this.checked = true
      this.indeterminate = false
    } else if (this.checkedChildNodes.length > 0) {
      this.checked = false
      this.indeterminate = true
    } else {
      this.checked = false
      this.indeterminate = false
      for (let i = 0; i < this.childNodes.length; i++) {
        if (this.childNodes[i].indeterminate) {
          this.indeterminate = true
          break
        }
      }
    }
    this.parent.handleChildCheckChange()
  }
  appendChild(data, index) {
    let isLeaf = false
    this.isLeaf = false
    if (data.hasOwnProperty(this.tree.prop.leaf)) isLeaf = data[this.tree.prop.leaf]
    if (data.hasOwnProperty(this.tree.prop.key)) {
      if (this.tree.nodesMap.get(data[this.tree.prop.key])) {
        throw new Error('节点的key不能重复')
      }
    }
    const child = new Node({
      parent: this,
      data: data,
      index: this.childNodes.length,
      tree: this.tree,
      isLeaf
    })
    if (index) {
      this.childNodes.splice(index, 0, child)
    } else {
      this.childNodes.push(child)
    }
    if (this.parent) this.parent.handleChildCheckChange()
  }
  remove() {
    let node = this
    const index = node.parent.childNodes.indexOf(node)
    node.parent.childNodes.splice(index, 1)
    node.parent.handleChildCheckChange()
    node.tree.nodesMap.delete(node.id)
    node = null
  }
}
